#include <networklib.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/select.h>
#include <netdb.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include  <malloc.h>

/*
 * new_socket:
 *
 * This acts as the instance creation method. It initialises the func
 * ptrs in the structure to the various functions to which they point
 * and sets the socket_fd to 0.
 *
 * returns a pointer to socket_info structure.
 *
 */

sock_info new_socket()
{
  sock_info self = malloc(sizeof(struct socket_info));
  self->create_connection = &create_conn;
  self->close_connection = &close_conn;
  self->send = &send_data;
  self->recv = &recv_data;
  self->socket_fd = 0;
  return self;
}


/*
 * create_conn
 *
 * sock_info self:
 *             A self reference to the structure on which this 
 *             method is called.
 *
 * const char* ip_addr:
 *            The IPv4 address of the host to connect to.
 *
 * int port:
 *            The port on the host to connect to.
 *
 * int ip_ver:
 *            The version of IP used. Either 4 or 6. Currently
 *            only 4 is supported.
 *
 * The function creates a socket using socket(2) and sets it to the self ptr.
 * It also establishes the connection with the server.
 *
 */
int create_conn(sock_info self, const char* ip_addr, int port, int ip_ver)
{
  int sock = 0;
  int error = 0;
  int socket_domain = AF_INET;
  int socket_type = SOCK_STREAM;
  int socket_proto = 0;
  int ip_version = 4;
  int conn_resp = 0;
  struct sockaddr_in server_addr;
  struct sockaddr_in server_addr_v6;
  struct hostent server;

  sock = socket (socket_domain, socket_type, socket_proto);
  if ( sock < 0 ) {
    error = errno;
    perror ("Couldn't create socket\n");
    return error;
  }
  
  /* Socket created successfully. Assing it to struct's socket_fd */
  self->socket_fd = sock;
  
  /* zero fill the struct. */
  memset(&server_addr, 0, sizeof(server_addr));

  /* Now update the sock_struct */
  server_addr.sin_family = socket_domain;
  server_addr.sin_port = htons(port);
  inet_pton(socket_domain, ip_addr, &server_addr.sin_addr);

  conn_resp = connect(sock,
		      (struct sockaddr*)&server_addr,
		      sizeof(server_addr));

  if (conn_resp < 0 ) {
    error = errno;
    perror("Error in connecting\n");
    return error;
  }

  return 0;
}


/*
 * send_data:
 *
 *
 * sock_info self:
 *                A self reference ptr.
 *
 * const char* data:
 *               The data that is to be sent to the server.
 *
 * This method repeteadly calls the send(2) function to send
 * all its data.
 *
 * RETURN: int
 *
 * Returns the total bytes sent across.
 *
 */
int send_data (sock_info self, const char* data)
{
  char* l_ptr = (char*) malloc (512);
  char* temp;
  int bytes_sent = 0;
  int data_size = strlen(data);
  if ( data_size <= 512) {
    bytes_sent = send(self->socket_fd, data, strlen(data), 0);
  } else {
    while (data_size >= 512) {
      memcpy(l_ptr, data, 512);
      data_size -= 512;
      bytes_sent += send(self->socket_fd, l_ptr, 512, 0);
      data += 512;
      if ( data_size <= 512 && data_size != 0) {
	memcpy(l_ptr, data, data_size);
	bytes_sent += send(self->socket_fd, l_ptr, data_size, 0);
	break;
      }
    }
  }
  return bytes_sent;
}


/* DUMMY IMPLEMENTATION */
 
char* recv_data (sock_info self)
{
  return "Tested";
}

/* DUMMY IMPLEMENTATION */
int close_conn (sock_info self)
{
  self->socket_fd = 0;
}
